﻿using System;
using System.Collections.Generic;
using System.Text;

namespace X3DLib
{
    public abstract class ParticleSystem : X3dInterfaceObject
    {
        private string Color;
        /// <summary>
        /// 基本颜色
        /// </summary>
        public string color
        {
            get { return Color; }
            set { Color = value; }
        }
        private string Num;
        /// <summary>
        /// 大小
        /// </summary>
        public string num
        {
            get { return Num; }
            set { Num = value; }
        }
        private string Range;
        /// <summary>
        /// 范围
        /// </summary>
        public string range
        {
            get { return Range; }
            set { Range = value; }
        }
        private string Life;
        /// <summary>
        /// 生命
        /// </summary>
        public string life
        {
            get { return Life; }
            set { Life = value; }
        }
        private string Gravity;
        /// <summary>
        /// 重力加速
        /// </summary>
        public string gravity
        {
            get { return Gravity; }
            set { Gravity = value; }
        }
        private string Wind;
        /// <summary>
        /// 风速
        /// </summary>
        public string wind
        {
            get { return Wind; }
            set { Wind = value; }
        }
        private string NodeName;
        /// <summary>
        /// 定义节点的名称
        /// </summary>
        public string nodeName
        {
            get { return NodeName; }
            set { NodeName = value; }
        }

        private string Size;
        /// <summary>
        /// 粒子大小
        /// </summary>
        public string size
        {
            get { return Size; }
            set { Size = value; }
        }
        /// <summary>
        /// 定义环境节点 重力加速度，风力等
        /// </summary>
        public object defEnvironment()
        {
            ProtoDeclare pd = new ProtoDeclare();
            pd.name = "Environment";
            ProtoInterface pi = new ProtoInterface();
            pd.protoInterface = pi;
            //主体大小
            pi.addChildren(new field("size", "SFFloat", "inputOutput"));
            //主体颜色
            pi.addChildren(new field("color", "SFColor", "inputOutput"));
            //范围
            pi.addChildren(new field("range", "SFVec3f", "inputOutput"));
            //粒子生命
            pi.addChildren(new field("life", "SFInt32", "inputOutput"));
            //各个方向的速度
            pi.addChildren(new field("gravity", "SFVec3f", "inputOutput"));
            //各个方向的风速
            pi.addChildren(new field("wind", "SFVec3f", "inputOutput"));
            Group g = new Group() { DEF = "declare_env" };
            pd.protoBody.addChildren(new Transform() { DEF = "empty" });
            g.addChildren(pd);
            return g;
        }
        /// <summary>
        /// 定义粒子
        /// </summary>
        /// <returns></returns>
        public object defParticle()
        {
            //ProtoInterface
            ProtoInterface pi = new ProtoInterface();
            /*粒子的各种属性可以通过如下接口输入，从而实现粒子的静态属性的设置*/
            //Material 属性
            pi.addChildren(new field("size", "SFVec3f", "inputOutput"));//设置大小
            pi.addChildren(new field("position", "SFVec3f", "inputOutput"));//设置位置
            pi.addChildren(new field("ambientIntensity", "SFFloat", "inputOutput"));
            pi.addChildren(new field("diffuseColor", "SFColor", "inputOutput"));
            pi.addChildren(new field("emissiveColor", "SFColor", "inputOutput"));
            pi.addChildren(new field("shininess", "SFFloat", "inputOutput"));
            pi.addChildren(new field("specularColor", "SFColor", "inputOutput"));
            pi.addChildren(new field("transparency", "SFFloat", "inputOutput"));
            //附加属性
            pi.addChildren(new field("velocity", "SFVec3f", "inputOutput"));//速度
            pi.addChildren(new field("age", "SFInt32", "inputOutput"));//年龄
            //ProtoBody add group
            ProtoBody pb = new ProtoBody();
            Group g = new Group();
            pb.addChildren(g);

            Transform t = new Transform();
            g.addChildren(t);

            t.DEF = "t1";
            //transform add <IS/><Shape/>
            IS is_tp = new IS();
            is_tp.addChildren(new connect("scale", "size"));
            is_tp.addChildren(new connect("translation", "position"));
            t.addChildren(is_tp);

            Shape shape = new Shape();
            Material m = new Material();
            IS is_mdc = new IS();
            is_mdc.addChildren(new connect("ambientIntensity", "ambientIntensity"));
            is_mdc.addChildren(new connect("diffuseColor", "diffuseColor"));
            is_mdc.addChildren(new connect("emissiveColor", "emissiveColor"));
            is_mdc.addChildren(new connect("shininess", "shininess"));
            is_mdc.addChildren(new connect("specularColor", "specularColor"));
            is_mdc.addChildren(new connect("transparency", "transparency"));
            m.addChildren(is_mdc);
            Appearance app = new Appearance(m);
            shape.appearance = app;
            //加入原型节点
            shape.addChildren(this.baseParticle);
            t.addChildren(shape);

            Group pd_g = new Group();
            pd_g.DEF = "declare_particle";//包裹整个节点定义
            ProtoDeclare pd = new ProtoDeclare(pb, pi);//节点定义
            pd.name = this.nodeName+"_Node";
            pd_g.addChildren(pd);
            return pd_g;
        }
        /// <summary>
        /// 创建环境
        /// </summary>
        /// <param name="name">节点名称</param>
        /// <param name="color">主体颜色</param>
        /// <param name="range">活动范围</param>
        /// <param name="gravity">重力加速度</param>
        /// <param name="wind">风速</param>
        /// <returns>环境对象 （包括：自身限制和外界限制）</returns>
        public object createEnvironment(string name,string size, string color, string range,string life, string gravity, string wind)
        {
            return new Environment.ProtoInstance(name ,size ,color , range, life, gravity ,wind);
        }
        /// <summary>
        /// 子类通过重写此方法来实现加入子类后期定义的节点实例,以丰富粒子系统实例
        /// </summary>
        /// <returns></returns>
        public virtual void addSubclassProtoInstances(ref Transform instanceGroup){}
        /// <summary>
        /// 创建粒子
        /// </summary>
        /// <param Name="num">个数</param>
        /// <returns></returns>
        public object createParticles(int num)
        {
            //改为Transform 来封装有利于以后整体移动
            Transform  g = new Transform ();
            g.DEF = this.nodeName+"_group";
            //环境节点为第一个[0]
            //父类的实例
            g.addChildren(createEnvironment("Environment",this.size, this.color, this.range, this .life , this .gravity , this .wind ));
            for (int i = 0; i < num; i++)
            {
                g.addChildren(new ProtoInstance(this.nodeName+"_Node") {});
            }
            //添加子类的实例
            addSubclassProtoInstances(ref g);
            //g.addChildren(addSubclassProtoInstance());
            //添加TouchSensor
            g.addChildren(new TouchSensor(this.nodeName +"_touch"));
            return g;
        }
        /// <summary>
        /// 设置属性表
        /// </summary>
        /// <param Name="color">颜色</param>
        /// <param Name="size">大小</param>
        /// <param Name="range">范围</param>
        public void setAttributes(string color, string num, string range,string life,string gravity,string wind)
        {
            this.color = color;
            this.num = num;
            this.range = range;
            this.life = life;
            this.gravity = gravity;
            this.wind = wind;
            createParticlesSystem();
        }

        private Random rand = new Random();
        public string randValue(float[] f)
        {
            string s = "";
            for (int i = 0; i < f.Length; i++)
            {
                string temp = (rand.NextDouble() * f[i]).ToString();
                if (temp.Length >= 5) temp = temp.Remove(5);
                s += temp + " ";
            }
            return s;
        }
        /// <summary>
        /// 添加子类路由
        /// </summary>
        /// <param name="group"></param>
        public virtual void addSubClassRoutes(ref Group group,string nodeName) { }
        public virtual void addSubClassNodes() { }
        /// <summary>
        /// 根据属性创建粒子系统
        /// </summary>
        public void createParticlesSystem()
        {
            //清空重建
            this.clearChildren();
            this.addSubClassNodes();
            Group group = new Group();
            group.DEF = this.nodeName+"_system";
            group.addChildren(defEnvironment());
            group.addChildren(defParticle());
            group.addChildren(createParticles(Int32.Parse (this.num) ));
            group.addChildren(createScript());
            //timer
            TimeSensor time = new TimeSensor(this.nodeName +"_clock", "1", "true");
            ROUTE route_start = new ROUTE("fraction_changed", time.DEF, "start", this .nodeName +"_control");
            ROUTE route_touch = new ROUTE("isActive", this.nodeName + "_touch", "touch_change", this.nodeName + "_control");
            group.addChildren(time);
            group.addChildren(route_start);
            group.addChildren(route_touch);
            //添加子类路由
            this.addSubClassRoutes(ref group,this.nodeName);
            this.addChildren(group);
        }
        /// <summary>
        /// 添加子类脚本fields
        /// </summary>
        /// <param name="s"></param>
        public virtual void addSubClassScriptFields(ref Script s)
        {
        }
        /// <summary>
        /// 创建动画控制脚本
        /// </summary>
        /// <returns></returns>
        public object createScript()
        {
            Script s = new Script();
            s.DEF = this.nodeName + "_control";
            s.directOutput = "true";
            //粒子总体
            field f_g = new field("group", "SFNode", "inputOutput");
            f_g.addChildren(new Transform (this.nodeName +"_group"));
            s.addChildren(f_g);
            //粒子实例节点个数
            s.addChildren(new field("nodeNum", "SFInt32", "inputOutput"));
            //全部节点
            s.addChildren(new field("nodes", "MFNode", "inputOutput"));
            //主体颜色
            s.addChildren(new field("color", "SFColor", "inputOutput"));
            //活动范围
            s.addChildren(new field("range", "SFVec3f", "inputOutput"));
            //生命长度 60
            s.addChildren(new field("life", "SFInt32", "inputOutput"));
            //重力加速度
            s.addChildren(new field("gravity", "SFVec3f", "inputOutput"));
            //风速
            s.addChildren(new field("wind", "SFVec3f", "inputOutput"));
            //启动函数
            s.addChildren(new field("start", "SFFloat", "inputOnly"));
            //触碰函数
            s.addChildren(new field("touch_change", "SFBool", "inputOutput"));
            //子类加入节点的注释
            s.addChildren("<!-- add by subclass -->\n");
            addSubClassScriptFields(ref s);
            //添加脚本函数
            s.addChildren(createSubClassScript ());//只要实现了setScriptContext()
            return s;
        }

        public override  string getAttrs()
        {
            return "color|" + this.color + "|max:1 min:0,"
                    + "num|" + this.num + "|int32,"
                    + "range|" + this.range + "|float,"
                    + "life|" + this.life + "|int,"
                    + "gravity|" + this.gravity + "|float[3],"
                    + "wind|" + this.wind + "|float[3]";
        }

        public override  void setAttrs(string attrs)
        {
            string[][] temp = X3DString.getArrayByAttrs(attrs);
            for (int i = 0; i < temp.Length; i++)
            {
                switch (temp[i][0])
                {
                    case "color":
                        this.color = temp[i][1];
                        break;
                    case "num":
                        this.num = temp[i][1];
                        break;
                    case "range":
                        this.range = temp[i][1];
                        break;
                    case "life":
                        this.life = temp[i][1];
                        break;
                    case "gravity":
                        this.gravity = temp[i][1];
                        break;
                    case "wind":
                        this.wind = temp[i][1];
                        break;

                }
            }
            setAttributes(this.color, this.num, this.range,this .life ,this .gravity ,this .wind );
        }
        private string BaseParticle;
        /// <summary>
        /// 此处设计需要技巧
        /// 由于属性值必须为字符串时才不被加入文档，
        /// 所以节点的原始形态用字符串形式替代而不是一个节点对象
        /// 注:Switch方案在Octage中不可行
        /// </summary>
        public string baseParticle
        {
            get { return BaseParticle; }
            set { BaseParticle = value; }
        }
        /// <summary>
        /// 初始化参数  派生类必须重写此方法来设置默认值
        /// </summary>
        public virtual void initialize(){}
        /// <summary>
        /// 构造函数
        /// </summary>
        public ParticleSystem()
        {
            //所有的属性都有设置默认值，然后调用虚拟方法来覆盖默认值；派生类只要重写方法就可以达到各自所需效果
            this.nodeName = "Particle";
            this.num = "600";
            this.size = "0.025";
            this.color = "1 0 0";
            this.range = "1 1 1";
            this.life = "200";
            this.gravity = "0.0 9.8 0.0";
            this.wind = "0.6 0.1 0.2";
            this.baseParticle = new Sphere("1").toString();
            initialize();
            this.setAttrs("");
        }
        /// <summary>
        /// 派生类重写此方法来添加脚本控制
        /// </summary>
        /// <returns>脚本函数字符串</returns>
        public abstract string createSubClassScript();
    }
}
